package com.sh.d1.gatewayserver;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Netty 客户端
 */
public final class NettyClient {
    /**
     * 日志对象
     */
    static private final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class);
    /**
     * new NioEventLoopGroup 有一个添加线程数量的参数
     * 解决添加一台服务器启动一个线程，导致后台服务器多了以后网关的性能都被线程占用了，使用单例解决
     */
    static private final NioEventLoopGroup WORK_GROUP = new NioEventLoopGroup(1,
            (r)->{
                Thread t = new Thread(r);
                t.setName("=======HelloThread========");
                return  t;
    });

    /**
     * 木匾服务器id
     */
    private  String _targetServerId;

    /**
     * 网关服务器 到 游戏服务器 的 信道
     */
    private Channel _gameServerCh;

    /**
     * 关闭回调函数
     */
    private Function<Future<?>, Void> _closeCallBack;


    public NettyClient(String targetServerId) {
        _targetServerId = targetServerId;
    }

    /**
     * 获取目标服务器id
     * @return
     */
    public String getTargetServerId() {
        return _targetServerId;
    }

    /**
     * 设置关闭回调函数
     * @param val  关闭回调函数
     */

    public void putCloseCallBack(Function<Future<?>, Void> val) {
        _closeCallBack = val;
    }


    public void connect(String host, int port){
        connect(host,port,null);
    }

    /**
     * 连接服务器
     *
     * @param host 服务器 IP 地址
     * @param port 端口号
     */
    public void connect(String host, int port,NioEventLoopGroup workerGroup) {
        if (null == host ||
            port <= 0) {
            return;
        }

        String strUri = "ws://" + host + ":" + port + "/websocket";

        DefaultHttpHeaders  headers =new DefaultHttpHeaders();
        headers.set("gatewayServerId",GatewayServer.getId());

        try {
            final WebSocketClientHandshaker handShaker = WebSocketClientHandshakerFactory.newHandshaker(
                new URI(strUri),
                WebSocketVersion.V13,
                null,
                true,
                    headers

            );
            if(null == workerGroup){
                workerGroup = WORK_GROUP;
            }
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast(
                        new HttpClientCodec(), // Http 客户端编解码器
                        new HttpObjectAggregator(65535),
                        new WebSocketClientProtocolHandler(handShaker),
                        new InternalServerMsgHandler()
                    );
                }
            });
            b.option(ChannelOption.SO_KEEPALIVE, true);

            // 客户端开启
            ChannelFuture f = b.connect(host, port).sync();

            if (!f.isSuccess()) {
                return;
            }

            LOGGER.info(
                "连接到 gameServer, URI = {}",
                strUri
            );

            // 用 Cd 来等待握手
            final CountDownLatch cdL = new CountDownLatch(8);

            while (!cdL.await(200, TimeUnit.MILLISECONDS) &&
                !handShaker.isHandshakeComplete()) {
                // 在这里等待握手成功
                cdL.countDown();
            }

            if (!handShaker.isHandshakeComplete()) {
                LOGGER.error("握手失败");
                return;
            }

            LOGGER.info("握手成功");

            // 下面的这个 ch 就是 gatewayServer 到 gameServer 的一条通道
            _gameServerCh = f.channel();
        } catch (Exception ex) {
            // 记录错误日志
            LOGGER.error(ex.getMessage(), ex);
        }
    }

    /**
     * 发送消息对象
     *
     * @param msgObj 消息对象
     */
    public void sendMsg(Object msgObj) {
        if (null == msgObj ||
            null == _gameServerCh) {
            return;
        }

        // 写出消息
        _gameServerCh.writeAndFlush(msgObj);
    }
}
